/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.jbr.simplefoodplan.gwt.client;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestBuilder.Method;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

/**
 *
 * @author jbr
 */
public class  ServerCom {
    private static final String JSON_URL = "resources/";
    private static final String DISH_LIST = "dish/list";
    private static final String DISH = "dish/";
    private static final String PLAN_DATE = "list/atDate/";
    private static final String PLAN_LIST = "plan/list";
    private static final String PLAN_ADD = "plan/add";
    private static final String PLAN = "plan/";
    private static final String AUTH = "auth/";
    private static final String LOGOUT = "logoutUrl";


    private static void printArray(JsArray<Dish> array) {
        for (int i= 0; i < array.length(); i++) {
            System.out.println("Dish name: " + array.get(i).getName() + ", id: " + array.get(i).getId());
        }
    }

    private static RequestBuilder newBuilder(Method httpMethod, String url) {
        RequestBuilder builder = new RequestBuilder(httpMethod, url);
        builder.setHeader("Cache-Control","no-cache");
        builder.setHeader("Accept","application/json");
        return builder;
    }

    public static void getPlanList(final ListCallback<Plan> callback) {
        RequestBuilder builder = newBuilder(RequestBuilder.GET, JSON_URL + PLAN_LIST);
        System.out.println("Getting for URL: " + builder.getUrl());
        try {

            Request request = builder.sendRequest(null, new RequestCallback() {
            @Override
            public void onError(Request request, Throwable exception) {
                throw new RuntimeException(exception);
            }

            @Override
            public void onResponseReceived(Request request, Response response) {
              if (200 == response.getStatusCode()) {
                    final String text = response.getText();
                    System.out.println(text);
                        try {
                            final List<Plan> planList = asListOf(Plan.class, "plan", text);
                            System.out.println("Size of planlist: " +   planList.size());
                            callback.onResponseOk(planList);
                        } catch (RequestException ex) {
                            System.out.println(ex);
                        }
              } else {
                  System.out.println("Error " + response.getStatusText());
              }
            }
          });

        } catch (RequestException e) {
            System.out.println("Couldn't retrieve JSON " + e);
            //displayError("Couldn't retrieve JSON");
        }

    }

    public static void getDishList(final ListCallback<Dish> callback) {
    // Send request to server and catch any errors.
        RequestBuilder builder = newBuilder(RequestBuilder.GET, JSON_URL + DISH_LIST);

        System.out.println("Getting for URL: " + builder.getUrl());
        try {

            Request request = builder.sendRequest(null, new RequestCallback() {
            public void onError(Request request, Throwable exception) {
                throw new RuntimeException(exception);
            }

            public void onResponseReceived(Request request, Response response) {
              if (200 == response.getStatusCode()) {
                    final String text = response.getText();
                    System.out.println(text);
                        try {

                            final List<Dish> asListOf = asListOf(Dish.class, "dish", text);
                            Collections.sort(asListOf, new Comparator<Dish>() {

                                @Override
                                public int compare(Dish o1, Dish o2) {
                                    final Integer lu1 = o1.getLastUsageInDays()==null ? 20000 : new Integer(o1.getLastUsageInDays());
                                    final Integer lu2 = o2.getLastUsageInDays()==null ? 20000 : new Integer(o2.getLastUsageInDays());

                                    return lu1.compareTo(lu2);
                                }
                            });

                            //printArray(dishList);
                            System.out.println("Size of dishlist: " + asListOf.size());
                            callback.onResponseOk(asListOf);
                        } catch (RequestException ex) {
                            System.out.println(ex);
                        }
              } else {
                  System.out.println("Error " + response.getStatusText());
                //displayError("Couldn't retrieve JSON (" + response.getStatusText()
                //    + ")");
              }
            }
          });
          
        } catch (RequestException e) {
            System.out.println("Couldn't retrieve JSON " + e);
            //displayError("Couldn't retrieve JSON");
        }
    }

    public static void getLogoutUrl(final StringCallback callback) {
        RequestBuilder builder = newBuilder(RequestBuilder.GET, JSON_URL + AUTH + LOGOUT);
        System.out.println("Getting for URL: " + builder.getUrl());
        try {

            Request request = builder.sendRequest(null, new RequestCallback() {
            public void onError(Request request, Throwable exception) {
                System.out.println("Error: in getLogoutUrl");
                throw new RuntimeException(exception);
            }

            public void onResponseReceived(Request request, Response response) {
              if (200 == response.getStatusCode()) {
                    final String text = response.getText();
                    System.out.println("Logout url:" + text);
                        callback.onResponseOk(text);
              } else {
                  System.out.println("Error " + response.getStatusText());
                //displayError("Couldn't retrieve JSON (" + response.getStatusText()
                //    + ")");
              }
            }
          });

        } catch (RequestException e) {
            System.out.println("Couldn't retrieve JSON " + e);
            //displayError("Couldn't retrieve JSON");
        }

    }



    /**
    * Convert the string of JSON into JavaScript object.
    */
    public static <T extends JavaScriptObject> JsArray<T>
            asArrayOf(Class<T> c, String jsonObjectName, String jsonString) throws RequestException {
        JSONValue jv = JSONParser.parseStrict(jsonString);
        JSONObject o = jv.isObject();
        if (o == null || !o.containsKey(jsonObjectName))
            throw new RequestException("No " + jsonObjectName + " object found in return string from server");
        else {
            final JSONValue get = o.get(jsonObjectName);
            return  (JsArray) get.isArray().getJavaScriptObject();
        }
    }

    public static <T extends JavaScriptObject> List<T>
            asListOf(Class<T> c, String jsonObjectName, String jsonString) throws RequestException {
        JSONValue jv = JSONParser.parseStrict(jsonString);
        JSONObject o = jv.isObject();
        if (o == null || !o.containsKey(jsonObjectName))
            throw new RequestException("No " + jsonObjectName + " object found in return string from server");

        final List<T> list = new ArrayList<T>();
        final JSONValue get = o.get(jsonObjectName);
        if (get.isArray() == null)
            list.add((T) get.isObject().getJavaScriptObject());
        else {
            final JsArray jsArray = (JsArray)get.isArray().getJavaScriptObject();
            for (int i= 0; i < jsArray.length(); i++)
                list.add((T) jsArray.get(i));
        }
        return list;
    }

    public static <T extends JavaScriptObject> List<T> asListOf(JsArray<T> jsArray) {
        List<T> ret = new ArrayList<T>(jsArray.length());
        for (int i= 0; i < jsArray.length(); i++)
            ret.add(jsArray.get(i));
        return ret;
    }

    public static void remove(Dish dish, final RemoveCallback callback) {
        RequestBuilder builder = newBuilder(RequestBuilder.DELETE, JSON_URL + DISH + dish.getId());
        System.out.println("Deleting for URL: " + builder.getUrl());
        try {

            builder.sendRequest(null, new RequestCallback() {
            public void onError(Request request, Throwable exception) {
                throw new RuntimeException(exception);
            }

            public void onResponseReceived(Request request, Response response) {
                if (204 == response.getStatusCode()) {
                    System.out.println("Ok. Dish removed");
                    callback.onResponseOk();
                } else {
                    System.out.println("Error " + response.getStatusText());
                //displayError("Couldn't retrieve JSON (" + response.getStatusText()
                //    + ")");
              }
            }
          });

        } catch (RequestException e) {
            System.out.println("Couldn't retrieve JSON " + e);
            //displayError("Couldn't retrieve JSON");
        }
    }

    public static void remove(Plan plan, final RemoveCallback callback) {
        RequestBuilder builder = newBuilder(RequestBuilder.DELETE, JSON_URL + PLAN + plan.getId());
        System.out.println("Deleting for URL: " + builder.getUrl());
        try {
            builder.sendRequest(null, new RequestCallback() {
            public void onError(Request request, Throwable exception) {
                throw new RuntimeException(exception);
            }

            public void onResponseReceived(Request request, Response response) {
                if (204 == response.getStatusCode()) {
                    System.out.println("Ok. Plan removed");
                    callback.onResponseOk();
                } else {
                    System.out.println("Error " + response.getStatusText());
                //displayError("Couldn't retrieve JSON (" + response.getStatusText()
                //    + ")");
              }
            }
          });

        } catch (RequestException e) {
            System.out.println("Couldn't retrieve JSON " + e);
            //displayError("Couldn't retrieve JSON");
        }
    }

    public static void add(final Integer index, Plan plan, final ElementCallback<Plan, Integer> callback) {
        RequestBuilder builder = newBuilder(RequestBuilder.POST,
                JSON_URL + PLAN_ADD);
        System.out.println("Post to URL: " + builder.getUrl() + " content:" + plan.getDishId() + " " + new JSONObject(plan).toString());
        try {
            builder.setHeader("Content-type", "application/json");
            Request request = builder.sendRequest(new JSONObject(plan).toString(), new RequestCallback() {
                public void onError(Request request, Throwable exception) {
                    throw new RuntimeException(exception);
                }

                public void onResponseReceived(Request request, Response response) {
                    if (200 == response.getStatusCode()) {
                        final String text = response.getText();
                        System.out.println(text);
                        if (text == null || text.equalsIgnoreCase("null"))
                            callback.onResponseOk(null, index);
                        else {
                            JSONValue jv = JSONParser.parseStrict(text);
                            callback.onResponseOk((Plan)jv.isObject().getJavaScriptObject(), index);
                        }
                    } else {
                        System.out.println("Error " + response.getStatusText());
                        //displayError("Couldn't retrieve JSON (" + response.getStatusText()
                        //    + ")");
                    }
                };
            });

        } catch (RequestException e) {
            System.out.println("Couldn't retrieve JSON " + e);
            //displayError("Couldn't retrieve JSON");
        }
        
    }

    public static void put(final Integer index, Dish olddish, final ElementCallback<Dish, Integer> callback) {
        // Flush out all unnessesary fields from dish, simply by creating a new one
        Dish dish = (Dish)JavaScriptObject.createObject();;
        dish.setId(olddish.getId());
        dish.setName(olddish.getName());
        RequestBuilder builder = newBuilder(RequestBuilder.PUT,
                JSON_URL + DISH + dish.getId());
        System.out.println("PUT to URL: " + builder.getUrl() + " content:" + dish.getId() + " " + new JSONObject(dish).toString());
        try {
            builder.setHeader("Content-type", "application/json");
            Request request = builder.sendRequest(new JSONObject(dish).toString(), new RequestCallback() {
                public void onError(Request request, Throwable exception) {
                    throw new RuntimeException(exception);
                }

                public void onResponseReceived(Request request, Response response) {
                    if (200 == response.getStatusCode()) {
                        final String text = response.getText();
                        System.out.println(text);
                        if (text == null || text.equalsIgnoreCase("null"))
                            callback.onResponseOk(null, index);
                        else {
                            JSONValue jv = JSONParser.parseStrict(text);
                            callback.onResponseOk((Dish)jv.isObject().getJavaScriptObject(), index);
                        }
                    } else {
                        System.out.println("Error " + response.getStatusText());
                        //displayError("Couldn't retrieve JSON (" + response.getStatusText()
                        //    + ")");
                    }
                };
            });

        } catch (RequestException e) {
            System.out.println("Couldn't retrieve JSON " + e);
            //displayError("Couldn't retrieve JSON");
        }
    }

    public static void getPlanAtDate(final Integer index, final Date date, final ElementCallback<Plan, Integer> callback) {
        RequestBuilder builder = newBuilder(RequestBuilder.GET,
                JSON_URL + PLAN + PLAN_DATE + MainEntryPoint.jsonFormat.format(date));
        builder.setHeader("Cache-Control","no-cache");
        builder.setHeader("Accept","application/json");
        System.out.println("Getting for URL: " + builder.getUrl());
        try {

            Request request = builder.sendRequest(null, new RequestCallback() {
            public void onError(Request request, Throwable exception) {
                throw new RuntimeException(exception);
            }

            public void onResponseReceived(Request request, Response response) {
              if (200 == response.getStatusCode()) {
                    final String text = response.getText();
                    System.out.println(text);
                    if (text == null || text.equalsIgnoreCase("null"))
                        callback.onResponseOk(null, index);
                    else {
                        try {
                            final List<Plan> asListOf = asListOf(Plan.class, "plan", text);
                            //printArray(dishList);
                            System.out.println("Size of planlist: " + asListOf.size());
                            if (asListOf != null && !asListOf.isEmpty())
                                callback.onResponseOk(asListOf.get(0), index);
                            else
                                callback.onResponseOk(null, index);
                        } catch (RequestException ex) {
                            System.out.println(ex);
                        }
                    }
              } else {
                  System.out.println("Error " + response.getStatusText());
                //displayError("Couldn't retrieve JSON (" + response.getStatusText()
                //    + ")");
              }
            }
          });

        } catch (RequestException e) {
            System.out.println("Couldn't retrieve JSON " + e);
            //displayError("Couldn't retrieve JSON");
        }
    }

    public interface ListCallback<T> {
        void onResponseOk(List<T> list);
    }

    public interface RemoveCallback {
        void onResponseOk();
    }

    public interface StringCallback {
        void onResponseOk(String string);
    }

    public interface ElementCallback<T extends JavaScriptObject, B> {
        void onResponseOk(T element, B backref);
    }

}
